package br.usp.poli.eml;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Set;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import br.usp.poli.entity.DataSetMetadata;
import br.usp.poli.entity.Experiment;
import br.usp.poli.entity.Keyword;
import br.usp.poli.entity.Person;
import br.usp.poli.entity.Variable;
import eml.ecoinformatics_org.attribute_2_0.AttributeListType;
import eml.ecoinformatics_org.attribute_2_0.AttributeType;
import eml.ecoinformatics_org.attribute_2_0.NumberType;
import eml.ecoinformatics_org.attribute_2_0.NumericDomainType;
import eml.ecoinformatics_org.attribute_2_0.PrecisionType;
import eml.ecoinformatics_org.attribute_2_0.UnitDictionary;
import eml.ecoinformatics_org.attribute_2_0.UnitType;
import eml.ecoinformatics_org.attribute_2_0.AttributeType.MeasurementScale;
import eml.ecoinformatics_org.attribute_2_0.AttributeType.MissingValueCode;
import eml.ecoinformatics_org.attribute_2_0.AttributeType.MeasurementScale.Datetime;
import eml.ecoinformatics_org.attribute_2_0.AttributeType.MeasurementScale.Ratio;
import eml.ecoinformatics_org.coverage_2_0.Coverage;
import eml.ecoinformatics_org.coverage_2_0.SingleDateTimeType;
import eml.ecoinformatics_org.coverage_2_0.TemporalCoverage;
import eml.ecoinformatics_org.coverage_2_0.TemporalCoverage.RangeOfDates;
import eml.ecoinformatics_org.dataset_2_0.DatasetType;
import eml.ecoinformatics_org.datatable_2_0.DataTableType;
import eml.ecoinformatics_org.eml_2_0.Eml;
import eml.ecoinformatics_org.party_2_0.ObjectFactory;
import eml.ecoinformatics_org.party_2_0.ResponsibleParty;
import eml.ecoinformatics_org.physical_2_0.PhysicalType;
import eml.ecoinformatics_org.physical_2_0.PhysicalType.DataFormat;
import eml.ecoinformatics_org.physical_2_0.PhysicalType.DataFormat.TextFormat;
import eml.ecoinformatics_org.physical_2_0.PhysicalType.DataFormat.TextFormat.SimpleDelimited;
import eml.ecoinformatics_org.resource_2_0.ScopeType;
import eml.ecoinformatics_org.software_2_0.SoftwareType.KeywordSet;

public class EmlFactory {

	public static Eml createEml(File file) {
		Eml result = null;
		try {
			JAXBContext ctx = JAXBContext
					.newInstance(new Class[] { Eml.class });
			Unmarshaller um = ctx.createUnmarshaller();
			result = (Eml) um.unmarshal(file);
		} catch (JAXBException e) {
			e.printStackTrace();
		}
		return result;
	}

	public static Eml createEml(Experiment experiment) {
		Eml eml = new Eml();
		definePackageAndSystem(eml);
		defineDataSet(experiment, eml);

		return eml;
	}

	private static void defineDataSet(Experiment experiment, Eml eml) {
		DatasetType emlDataset = new DatasetType();
		eml.setDataset(emlDataset);
		DataSetMetadata expDataSetMetadata = experiment.getDataSetMetadata();
		emlDataset.getTitle().add("" + expDataSetMetadata.getTitle());
		emlDataset.setShortName(expDataSetMetadata.getShortName());
		Set<Person> creators = expDataSetMetadata.getCreator();
		for (Person p : creators) {
			System.out.println(p.getSurName());
			ResponsibleParty responsibleParty = createResponsibleParty(p);
			emlDataset.getCreator().add(responsibleParty);
			emlDataset.getContact().add(responsibleParty);
		}
		insertKeywords(emlDataset, expDataSetMetadata);
		insertRangeOfDates(emlDataset, expDataSetMetadata);

		DataTableType dataTableType = new DataTableType();
		emlDataset.getDataTableOrSpatialRasterOrSpatialVector().add(dataTableType);
		dataTableType.setScope(ScopeType.DOCUMENT);
		dataTableType.setEntityName("%EntityName%");
		
		PhysicalType physicalType = new PhysicalType();
		dataTableType.getPhysical().add(physicalType);
		physicalType.setScope(ScopeType.DOCUMENT);
		physicalType.setObjectName("weblab-exported.csv");
		
		addDataFormat(physicalType);
		
		AttributeListType attributeListType = new AttributeListType();
		dataTableType.setAttributeList(attributeListType);
		List<AttributeType> attributeList = attributeListType.getAttribute();
		addDateAttribute(attributeList);
		List<Variable> variables = experiment.getVariables();
		for (Variable variable : variables) {
				AttributeType attributeType = getAttributeType(variable);
				attributeType.getAttributeLabel().add(variable.getInstrument().toString());
				attributeType.setAttributeDefinition("");
				attributeList.add(attributeType);
		}
		
	}

	private static AttributeType getAttributeType(Variable v) {
	    AttributeType attributeType = new AttributeType();
		
		attributeType.setAttributeName(v.getName());
		MissingValueCode missingValueCode = new MissingValueCode();
		missingValueCode.setCode(v.getMissingCodeValue());
		missingValueCode.setCodeExplanation(v.getMissingCodeValueExplanation());
		attributeType.getMissingValueCode().add(missingValueCode );
		MeasurementScale measurementScale = new AttributeType.MeasurementScale();
		attributeType.setMeasurementScale(measurementScale);
		Ratio ratio = new MeasurementScale.Ratio();
		measurementScale.setRatio(ratio);
		PrecisionType precision = new PrecisionType();
		precision.setValue(0.01f);
		ratio.setPrecision(precision);
		UnitType unitType = new UnitType();
		unitType.setStandardUnit(UnitDictionary.fromValue(v.getUnit()));
		ratio.setUnit(unitType);
		NumericDomainType numericDomainType = new NumericDomainType();
		numericDomainType.setNumberType(NumberType.fromValue(v.getNumberType()));
		ratio.setNumericDomain(numericDomainType);
		
		return attributeType;
	}

	private static void addDataFormat(PhysicalType physicalType) {
		DataFormat dataFormat = new PhysicalType.DataFormat();
		physicalType.setDataFormat(dataFormat);
		TextFormat textFormat = new DataFormat.TextFormat();
		dataFormat.setTextFormat(textFormat);
		textFormat.getRecordDelimiter().add("#x0A");
		textFormat.setAttributeOrientation("column");
		SimpleDelimited simpleDelimited = new SimpleDelimited();
		textFormat.setSimpleDelimited(simpleDelimited);
		simpleDelimited.getFieldDelimiter().add("#x2C");
	}

	private static void addDateAttribute(List<AttributeType> attributeList) {
		AttributeType expDateAttribute = new AttributeType();
		attributeList.add(expDateAttribute);
		expDateAttribute.setAttributeName("Date");
		expDateAttribute.getAttributeLabel().add("Date");
		expDateAttribute.setAttributeDefinition("Date of Sample");
		MeasurementScale dateAttributeMeasurementScale = new AttributeType.MeasurementScale();
		expDateAttribute.setMeasurementScale(dateAttributeMeasurementScale);
		Datetime datetimeMeasureScale = new MeasurementScale.Datetime();
		dateAttributeMeasurementScale.setDatetime(datetimeMeasureScale);
		datetimeMeasureScale.setFormatString("yyyy-MM-dd hh:mm:ss");
		datetimeMeasureScale.setDateTimePrecision("1 minute");
	}

	private static void definePackageAndSystem(Eml eml) {
		eml.setPackageId("username-"+Math.round(Math.random()*(Integer.MAX_VALUE)));
		eml.getSystem().add("knb");
	}

	private static void insertRangeOfDates(DatasetType emlDataset,
			DataSetMetadata expDataSetMetadata) {
		eml.ecoinformatics_org.coverage_2_0.ObjectFactory factory = new eml.ecoinformatics_org.coverage_2_0.ObjectFactory();
	
		TemporalCoverage temporalCoverage = factory.createCoverageTemporalCoverage();
		RangeOfDates rangeOfDates = factory.createTemporalCoverageRangeOfDates();
		temporalCoverage.setRangeOfDates(rangeOfDates);

		SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");

		SingleDateTimeType beginDate = new SingleDateTimeType();
		Date expBeginDate = expDataSetMetadata.getStartDate().getTime();
		beginDate.setCalendarDate(formatter.format(expBeginDate));
		rangeOfDates.setBeginDate(beginDate);

		SingleDateTimeType endDate = new SingleDateTimeType();
		Date expEndDate = expDataSetMetadata.getEndDate().getTime();
		endDate.setCalendarDate(formatter.format(expEndDate));
		rangeOfDates.setEndDate(endDate);

		Coverage coverage = factory.createCoverage();
		coverage.getGeographicCoverageOrTemporalCoverageOrTaxonomicCoverage()
				.add(temporalCoverage);
		emlDataset.setCoverage(coverage);
	}

	private static void insertKeywords(DatasetType emlDataset,
			DataSetMetadata expDataSetMetadata) {
		KeywordSet keywordSet = new KeywordSet();
		for (Keyword key : expDataSetMetadata.getKeywords()) {
			eml.ecoinformatics_org.software_2_0.SoftwareType.KeywordSet.Keyword keyword = new eml.ecoinformatics_org.software_2_0.SoftwareType.KeywordSet.Keyword();
			keyword.setValue(key.getValue());
			keywordSet.getKeyword().add(keyword);
		}
		emlDataset.getKeywordSet().add(keywordSet);
	}

	public static ResponsibleParty createResponsibleParty(Person person) {
		ObjectFactory factory = new ObjectFactory();
		eml.ecoinformatics_org.party_2_0.Person emlPerson = factory.createPerson();
		emlPerson.setSurName(person.getSurName());
		emlPerson.getGivenName().add(person.getGivenName());
		emlPerson.getSalutation().add(person.getSalutation());
		ResponsibleParty party = factory.createResponsibleParty();
		JAXBElement<eml.ecoinformatics_org.party_2_0.Person> individualName = factory
				.createResponsiblePartyIndividualName(emlPerson);
		party.getIndividualNameOrOrganizationNameOrPositionName().add(
				individualName);
		return party;
	}
}
